package fr.umlv.ig.adapter;

import java.util.ArrayList;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import fr.umlv.ig.event.TrainEvent;
import fr.umlv.ig.listener.TrainListener;
import fr.umlv.ig.model.Line;
import fr.umlv.ig.model.MetroModelImpl;
import fr.umlv.ig.model.Train;

/**
 * Adapter of the {@link MetroModelImpl} for represents the data in a tree.
 * With a TrainTreeModel you can just see the train and selected theme.
 * You can't remove a train.
 * @author jenseric
 * @author william
 *
 */
public class TrainTreeModel implements TreeModel {
	/**
	 * Modelof the adapter
	 */
	private final MetroModelImpl model;
	/**root of the tree.*/
	private final DefaultMutableTreeNode root = new DefaultMutableTreeNode();

	/**Listener who listen the model.*/
	private ArrayList<TreeModelListener> listeners=new ArrayList<TreeModelListener>();
	
	/**Boolean used for the listeners*/
	boolean firing=false;
	
	/**
	 * Initializes a TrainTreeModel
	 * @param model the model used by the adapter TraintreeModel.
	 */
	public TrainTreeModel(MetroModelImpl model) {
		this.model = model;
		model.addTrainListener(new TrainListener() {
			
			@Override
			public void trainChange(TrainEvent event) {
				someNodesHaveChanged((Train) event.getSource());
			}
		});
	}
	
	/**
	 * Calls when a node have change.
	 * @param train the train concerned by the event.
	 */
	protected void someNodesHaveChanged(Train train) {
		fireTreeNodeChanged(this,new Object[] {root},null,new Object[] {model.getLine(train)});
	}
	
	/**
	 * Fire to prevents every listeners of the adapter.
	 * @param source the object concerned by the event.
	 * @param path the path of the object concerned in the tree
	 * @param childIndices the indice child concerned
	 * @param children the children concerned by the event.
	 */
	protected void fireTreeNodeChanged(Object source, Object[] path, int[] childIndices, Object[] children) {
		firing=true;
		try {
			TreeModelEvent event=new TreeModelEvent(source,path,childIndices, children);
			for (TreeModelListener l:listeners) {
				l.treeStructureChanged(event);
			}
		} finally {
			firing=false;
		}
	}	
	
	

	@Override
	public Object getRoot() {
		return root;
	}

	
	@Override
	public Object getChild(Object parent, int index) {
		if(parent == root) {
			return model.getLine(index);
		}
		if(parent instanceof Line) {
			return model.getTrain((Line) parent, index);
		}
		throw new AssertionError("getChild is not supported to be invoked on a leaf");
	
	}

	
	@Override
	public int getChildCount(Object parent) {
		if(parent == root) {
			return model.getCountLine();
		}
		if(parent instanceof Line) {
			return model.getCountTrain((Line)parent);
		}
		throw new AssertionError("getChildCount is not supposed to be invoked on a leaf");
	
	}

	
	@Override
	public boolean isLeaf(Object node) {
		if(node==root || node instanceof Line) {
			return false;
		}
		return true;
	}

	
	@Override
	public void valueForPathChanged(TreePath path, Object newValue) {
		throw new UnsupportedOperationException();
	}

	
	@Override
	public int getIndexOfChild(Object parent, Object child) {
		if(parent == root) {
			int n = model.getCountLine();
			for(int i=0; i< n ; i++) {
				Line line = model.getLine(i);
				if(line.equals(child)) {
					return i;
				}
			}
		}
		if(parent instanceof Line) {
			Line line = (Line) parent;
			int n= model.getCountTrain(((Line) parent));
			for(int i = 0; i< n;i++) {
				if(((Train)child).equals(model.getTrain(line, i))) {
					return i;
				}
			}
		}
		throw new AssertionError("getIndexOfChild is not supposed to be invoked on a leaf");
	
	}

	
	@Override
	public void addTreeModelListener(TreeModelListener l) {
		 listeners.add(l);

	}

	
	@Override
	public void removeTreeModelListener(TreeModelListener l) {
        if (firing)
            throw new IllegalStateException("Cannot remove listeners while fire... is using them");
        listeners.remove(l);
	}
	
	
	/**
	 * Selects the object. this object is a Line or a Train.
	 * @param object the object selected.
	 */
	public void selected(Object object) {
		model.selected(object);
	}

}
